﻿using System;
using System.IO;
using System.Threading;
using BSDS.Core.Infrastructure.Logging;
using BSDS.Service.Configuration;

namespace BSDS.Service.Utils
{
    public class FileSystemWatcherAdapter
    {
        private readonly TimeSpan maxFileCopyTime;
        private readonly Logger logger;
        private readonly FileSystemWatcher fileWatcher;

        public event EventHandler<FileSystemEventArgs> Created;

        public bool EnableRaisingEvents
        {
            get { return fileWatcher.EnableRaisingEvents; }
            set { fileWatcher.EnableRaisingEvents = value; }
        }


        public FileSystemWatcherAdapter(string directoryPath, TimeSpan maxFileCopyTime, Logger logger)
        {
            this.maxFileCopyTime = maxFileCopyTime;
            this.logger = logger;

            fileWatcher = new FileSystemWatcher(directoryPath);
            fileWatcher.Created += FileCreationStartedHandler;
        }

        private void FileCreationStartedHandler(object sender, FileSystemEventArgs e)
        {
            if (IsNewFileCreated(e))
            {
                WaitUntilFileFullyCopied(e.FullPath, sender, e);
            }
        }

        private static bool IsNewFileCreated(FileSystemEventArgs e)
        {
            return File.Exists(e.FullPath) && e.ChangeType == WatcherChangeTypes.Created;
        }

        private void WaitUntilFileFullyCopied(string filePath, object sender, FileSystemEventArgs e)
        {
            bool isAvailable = false;

            // Wait for maxFileCopyTime BEFORE trying to open the file as this may break the upload (Fix for Edward Jones)
            Thread.Sleep(maxFileCopyTime);

            // Start the timer now (excluding above wait time deliberately so that the loop will execute).
            DateTime startTime = DateTime.Now;

            // By this point, the file should have been completely uploaded--but this is not a guarantee, so we will still support the loop below.
            while ((DateTime.Now - startTime < maxFileCopyTime) && !isAvailable)
            {
                try
                {
                    using (File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.None))
                    {
                        isAvailable = true;
                    }
                }
                catch
                {
                    logger.Log("Waiting for copy to finish: " + filePath);

                    Thread.Sleep(TimeSpan.FromSeconds(10));
                }
            }

            if (!isAvailable)
            {
                logger.Log("Could not access file in configured time period: " + maxFileCopyTime);
            }
            else
            {
                OnFileCreated(sender, e);
            }
        }

        private void OnFileCreated(object sender, FileSystemEventArgs e)
        {
            if (Created != null)
            {
                Created(sender, e);
            }
        }
    }
}